#include <stdio.h>
#include <sys/socket.h>
#include <sys/user.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <inttypes.h>
//#include <sys/reg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/personality.h>

void (*aaa)();

void null_func()
{
	printf("THIS is NULL!!!\n");
}

void map_and_call_null()
{
	char *addr = NULL;

	addr = mmap(0, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
	addr[0] = '\xff';
	addr[1] = '\x25';
	*(unsigned int *)&addr[2] = 6;
	*(unsigned long *)&addr[6] = (unsigned long)&null_func;
	aaa = NULL; //...NULL
	(*aaa)();

	int *empty = NULL;
	printf("1 %x\n",*empty);
	printf("2 %x\n",*(empty+1));
	printf("3 %x\n",*(empty+2));
}

int main()
{
	map_and_call_null();
}


/*

我们都知道 对于NULL指针的应用是不对的，然而更加深入的探讨NULL指针后你会发现：NULL其实只是一个概念性的东西，也就是说 如果我原因 我可以把任何地址规定为NULL，只要我对其进行处理就可以了。
 
那么我们会有一个疑问：既然NULL是形式上的概念 ，我们可以利用0地址呢？答案是当然可以。
1）首先，我们通过系统调用mmap 将线性地址的第一个PAGE 加入VMA。注意：必须带用参数MAP_FIXED 否则你系统的
[root@localhost ~]# cat /proc/sys/vm/mmap_min_addr
4096
这个变量值会限制你必须从第二页空闲的地方开始映射。
2）通过1，第一个线性地址page（0-4095），我们已经可以利用，我们写入我们要跳转去执行的函数 指令
 
        addr[0] = '\xff';
        addr[1] = '\x25';
        *(unsigned int *)&addr[2] = 6;
        *(unsigned long *)&addr[6] = (unsigned long)&null_func;
这里有几个地方需要说明一下：
        0xff25 这是无条件绝对跳转的opcode
        后面紧跟的四个字节：0x00000006 指示了jmp将要跳去的地址在哪。
        在这里jmp要跳去的地址存储在0x00000006线性地址中，而0x00000006线性地址存储的正是函数null_func的地址。
3)OK,一切准备就绪。main函数首先调用map_and_call_null(),然后调用aaa函数。此时eip寄存器的值就为0x00000000，CPU去执行0地址处的代码。而0地址处的代码为一绝对跳转指令。从而，去执行了null_func函数。
这就是所谓的hook吧。。。。。
 
而内核代码是如何处理NULL的呢？
 
我们知道，缺页时，会触发CPU发出一个PGFAULT缺页异常，而对于缺页异常，由函数do_page_fault进行处理。在将发生缺页异常的线性地址保存在address变量中后，函数转去__do_page_fault 进行处理。
由于存在相应的VMA（mmap函数调用的结果）所以函数不会转去bad_area_nosemaphore:进行处理。由于我们是读请求造成的缺页，调用handle_mm_fault进行处理，处理完成后，函数返回。
 
我们的函数也得以继续执行完成，不会被KILL掉。。no segment fault :)


*/
